﻿// E-Book Copier
// Copyright (C) 2009 Chuan-Gen Fan
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Project: http://code.google.com/p/ebookcopier/
// Contact Author:
//    website: http://chuangen.name/
// e-mail/msn: chuangen@live.cn

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace EbookCopier
{
    /// <summary>
    /// 使用 SendMessage 实现对阅览器的操作。
    /// </summary>
    /// <remarks>
    /// 1. 判断阅览器进程的状态：<br />
    ///    遍历所有窗体的标题，如果匹配，则认为拥有该窗体的进程即为阅览器进程。<br />
    /// 2. 发送翻页等命令：<br />
    ///    使用 SendMessage 模拟用户点击 菜单/工具栏 实现对阅览器的各种操作。<br />
    /// 显然，这种实现决定了必须针对特定版本的特定阅览器程序设计。
    /// </remarks>
    public abstract class WmCommandCmder : Cmder
    {
        #region Win32

        // The WM_COMMAND message is sent when the user selects a command item from a menu, 
        // when a control sends a notification message to its parent window, or when an 
        // accelerator keystroke is translated.
        const int WM_COMMAND = 0x111;
        const int WM_CTLCOLORBTN = 0x0135;

        const int WN_LBUTTONDOWN = 0x0201;

        // The FindWindow function retrieves a handle to the top-level window whose class name
        // and window name match the specified strings. This function does not search child windows.
        // This function does not perform a case-sensitive search.
        [DllImport("User32.dll")]
        static extern int FindWindow(string strClassName, string strWindowName);

        // The FindWindowEx function retrieves a handle to a window whose class name 
        // and window name match the specified strings. The function searches child windows, beginning
        // with the one following the specified child window. This function does not perform a case-sensitive search.
        [DllImport("User32.dll")]
        static extern int FindWindowEx(int hwndParent, int hwndChildAfter, string strClassName, string strWindowName);


        // The SendMessage function sends the specified message to a 
        // window or windows. It calls the window procedure for the specified 
        // window and does not return until the window procedure has processed the message. 
        [DllImport("User32.dll")]
        static extern Int32 SendMessage(
            IntPtr hWnd,               // handle to destination window
            int Msg,                // message
            uint wParam,             // first message parameter
            [MarshalAs(UnmanagedType.LPStr)] string lParam); // second message parameter

        [DllImport("User32.dll")]
        static extern Int32 SendMessage(
            IntPtr hWnd,               // handle to destination window
            int Msg,                // message
            uint wParam,             // first message parameter
            uint lParam);			// second message parameter


        delegate bool CallBack(int hwnd, int lParam);
        [DllImport("user32")]
        static extern int EnumWindows(CallBack x, int y);
        
        [DllImport("user32.dll", EntryPoint = "GetWindowText")]
        static extern int GetWindowText(
            IntPtr hwnd,
            StringBuilder lpString,
            int cch);




        [DllImport("user32.dll")]
        static extern bool IsWindowVisible(IntPtr hWnd);
        [DllImport("user32.dll", EntryPoint = "IsWindow")]
        static extern bool IsWindow(IntPtr hWnd);

        //[DllImport("user32.dll")]
        //public static extern int GetWindowText(int hwnd,
        //   StringBuilder buf, int nMaxCount);

        //[DllImport("user32.dll")]
        //public static extern int GetClassName(int hwnd,
        //   [MarshalAs(UnmanagedType.LPStr)] StringBuilder buf,
        //   int nMaxCount);

        //[DllImport("user32.dll")]
        //public static extern int GetWindowRect(int hwnd, ref RECT rc);

        //[DllImport("user32.dll")]
        //// 注意，运行时知道如何列集一个矩形
        //public static extern int GetWindowRect(int hwnd, ref Rectangle rc);
        //[DllImport("user32.dll", EntryPoint = "FindWindow")]
        //public static extern int FindWindow(string WinHandler, string WinTitle);
        //[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
        //public static extern int FindWindowEx(int PH, int CH, string CC, string CTitle);
        //[DllImport("user32.dll", EntryPoint = "EnumChildWindows")]
        //public static extern int EnumChildWindows(int H, EnumChildWindowsPro myCallBack, int L);
        //[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId")]
        //public static extern int GetWindowThreadProcessId(int H, int L);
        //[DllImport("user32.dll", EntryPoint = "GetWindowModuleFileName")]
        //public static extern int GetWindowModuleFileName(int H, StringBuilder P, int PathLen);

        //[DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
        //public static extern int OpenProcess(int Rights, bool B, int PId);
        #endregion

        /// <summary>
        /// 翻到首页 WM_COMMAND 消息的 wParam 参数。
        /// </summary>
        uint wParamFirstPage = 0;
        /// <summary>
        /// 翻到尾页 WM_COMMAND 消息的 wParam 参数。
        /// </summary>
        uint wParamLastPage = 0;
        /// <summary>
        /// 翻到上一页 WM_COMMAND 消息的 wParam 参数。
        /// </summary>
        uint wParamPrevPage = 0;
        /// <summary>
        /// 翻到下一页 WM_COMMAND 消息的 wParam 参数。
        /// </summary>
        uint wParamNextPage = 0;

        /// <summary>
        /// 刷新 阅览器进程 状态的定时器。
        /// </summary>
        Timer timerQuery = null;
        /// <summary>
        /// 初始化类 <see cref="WmCommandCmder"/> 的新实例。
        /// </summary>
        /// <param name="wParamFirstPage">翻到首页 WM_COMMAND 消息的 wParam 参数。</param>
        /// <param name="wParamLastPage">翻到尾页 WM_COMMAND 消息的 wParam 参数。</param>
        /// <param name="wParamPrevPage">翻到上一页 WM_COMMAND 消息的 wParam 参数。</param>
        /// <param name="wParamNextPage">翻到下一页 WM_COMMAND 消息的 wParam 参数。</param>
        public WmCommandCmder(uint wParamFirstPage, uint wParamLastPage, uint wParamPrevPage, uint wParamNextPage)
        {
            this.wParamFirstPage = wParamFirstPage;
            this.wParamLastPage = wParamLastPage;
            this.wParamPrevPage = wParamPrevPage;
            this.wParamNextPage = wParamNextPage;

            timerQuery = new Timer();
            timerQuery.Interval = 1000;
            timerQuery.Tick += new EventHandler(timerQuery_Tick);

            timerQuery.Enabled = true;
        }
        void timerQuery_Tick(object sender, EventArgs e)
        {
            if (hReaderWnd == IntPtr.Zero)
            {
                FindWindows();
            }
            else
            {
                if (!IsWindow(hReaderWnd))
                {// 窗口已经消毁
                    this.ReaderHandle = IntPtr.Zero;
                    FindWindows();
                }
                else if (!IsWindowVisible(hReaderWnd))
                {// 不可见，一般是隐藏了，或者，colse了
                    this.ReaderHandle = IntPtr.Zero;
                    FindWindows();
                }
                else
                {//窗口句柄依然有效
                }
                RefreshReaderState();
            }
        }

        IntPtr hReaderWnd = IntPtr.Zero;
        /// <summary>
        /// 获取或设置 阅览器主窗体句柄。
        /// </summary>
        public IntPtr ReaderHandle
        {
            get { return hReaderWnd; }
            set
            {
                if (hReaderWnd == value)
                    return;

                hReaderWnd = value;

                RefreshReaderState();
            }
        }
        /// <summary>
        /// 更新状态。
        /// </summary>
        void RefreshReaderState()
        {
            ReaderState newState = ReaderState.Unknown;
            if (hReaderWnd == IntPtr.Zero)
            {//未运行
                newState = ReaderState.NotRunning;
            }
            else
            {//运行中
                string docTitle = GetDocumentTitle();
                if (docTitle == null || docTitle.Trim() == "")
                    newState = ReaderState.RunningButNoDocument;
                else
                    newState = ReaderState.RunningAndOpenDocument;
            }

            if (readerState == newState)
                return;
            readerState = newState;
            base.OnReaderStateChanged(EventArgs.Empty);
        }


        ReaderState readerState = ReaderState.Unknown;
        /// <summary>
        /// 获取阅览器的状态。
        /// </summary>
        /// <value>阅览器的状态。</value>
        public override ReaderState ReaderState
        {
            get { return readerState; }
        }
        void FindWindows()
        {
            CallBack myCallBack = new CallBack(Report); 
            EnumWindows(myCallBack, 0); 
        }
        bool Report(int hwnd, int lParam)
        {
            StringBuilder s = new StringBuilder(512);
            int i = GetWindowText((IntPtr)hwnd, s, s.Capacity);

            if (MatchWindowText(s.ToString()))
            {//找到了
                this.ReaderHandle = (IntPtr)hwnd;
            }

            return true;
        }
        /// <summary>
        /// 字符串是否匹配要查找的窗口标题。
        /// </summary>
        /// <param name="text">要判断的窗口标题</param>
        /// <returns>True=匹配; False=不匹配</returns>
        protected abstract bool MatchWindowText(string text);
        /// <summary>
        /// 获取主窗体的标题。
        /// </summary>
        /// <returns>主窗体的标题。</returns>
        protected string GetWindowText()
        {
            StringBuilder s = new StringBuilder(512);
            int i = GetWindowText(hReaderWnd, s, s.Capacity);
            return s.ToString();
        }
        /// <summary>
        /// 设置为 单页模式。对 Apabi Reader v3.2 有效。
        /// </summary>
        public void SetSinglePageMode()
        {
            if (hReaderWnd == IntPtr.Zero)
                return;
            SendMessage(hReaderWnd, WM_COMMAND, 0x00008009, 0x00000000);
        }
        /// <summary>
        /// 翻到上一页。
        /// </summary>
        public override void PrevPage()
        {
            if (hReaderWnd == IntPtr.Zero)
                return;
            SendMessage(hReaderWnd, WM_COMMAND, wParamPrevPage, 0x00000000);
        }
        /// <summary>
        /// 翻到下一页。
        /// </summary>
        public override void NextPage()
        {
            if (hReaderWnd == IntPtr.Zero)
                return;
            SendMessage(hReaderWnd, WM_COMMAND, wParamNextPage, 0x00000000);
        }
        /// <summary>
        /// 翻到首页。
        /// </summary>
        public void FirstPage()
        {
            if (hReaderWnd == IntPtr.Zero)
                return;
            SendMessage(hReaderWnd, WM_COMMAND, wParamFirstPage, 0x00000000);
        }
        /// <summary>
        /// 翻到尾页。
        /// </summary>
        public void LastPage()
        {
            if (hReaderWnd == IntPtr.Zero)
                return;
            SendMessage(hReaderWnd, WM_COMMAND, wParamLastPage, 0x00000000);
        }
    }
}